# -*- mode: python -*-
#
#

# system imports
import time

# DDB imports
from ddb.log import log, logException
from ddb.tools import localImport
from ddb.config import configFile
from ddb.user import userManager
from ddb.conference import conferenceManager
from ddb.module import moduleManager
from ddb.event import event
from ddb.lang import _

# Twisted imports
from twisted.internet import protocol, reactor

class ITransport:
    capabilities = {
        'conference': False
    }

    def __init__(self, manager, name, config):
        self.manager = manager
        self.config  = config
        self.run     = False
        self.name    = name

    def start(self):
        self.run = True
        log.msg(_('%s has no start method defined') % str(self.__class__))
        return False

    def stop(self):
        self.run = False
        log.msg(_('%s has no stop method defined') % str(self.__class__))
        return False

class ITransportProtocol:
    """Basic protocol skell."""

    type      = 'BASIC'
    lag       = 0.0
    usePrefix = True    # this transport uses a prefix to disinguish 
                        # between messages and commands
    multiLine = False   # does this transport support multiline output?

    def __init__(self):
        self.identity    = ['<SELF>', '']
        self.name        = '%x' % abs(id(self))
        self.online      = False
        self.conferences = conferenceManager()
        self.modules     = moduleManager
        self.idx         = ':'.join([self.type, self.name])
        self.users       = userManager(self.idx, *self.identity)
        self.callbacks   = {}

    #
    # Generic connection-related
    #

    def connectionMade(self):
        self.name   = self.factory.name
        self.online = True
        log.msg(_('Transport %s is now online') % str(self.__class__))

    def connectionLost(self, reason = ''):
        self.online = False
        log.msg(_('Transport %s is now offline') % str(self.__class__))
        self.users.flush()
        self.conferences.flush()

    def reply(self, message, e=None):
        pass

    def say(self, target, message):
        pass

    #
    # Status updates about myself
    #

    def signedOn(self):
        log.msg(_('I signed on to the server as %s') % self.identity[0])
        event(self, 'signedOn') 

    def openedConference(self, target):
        log.msg(_('I joined conference at %s') % (str(target)), debug=True)
        self.conferences.add(target)
        self.conferences.userAdd(target, self.users.users['<SELF>'])

    def closedConference(self, target):
        log.msg(_('I parted conference at %s') % (str(target)), debug=True)
        self.conferences.userRemove(target, self.users.users['<SELF>'])
        self.conferences.remove(target)

    def message(self, senderIdentity, senderMask, target, message):
        log.msg(_('Message from %s on %s: %s') % (senderIdentity, target, message), debug=True)
        user = self.users.update(senderIdentity, target)
        event(self, 'message', user=user, target=target, message=message)

    #
    # Status updates about others
    #

    def joinedConference(self, senderIdentity, senderMask, target):
        log.msg(_('User %s (%s) joined conference at %s') % (str(senderIdentity), senderMask, str(target)), debug=True)
        user = self.users.add(senderIdentity, senderMask)
        self.conferences.userAdd(target, user)
        event(self, 'joinedConference', user=user, target=target)
        del user

    def partedConference(self, senderIdentity, senderMask, target, reason=''):
        log.msg(_('User %s parted conference at %s') % (str(senderIdentity), str(target)), debug=True)
        # check if this user still shares any conference with me, if not, remove entry
        # on transports like IRC we don't get online/offline notifications if the
        # user does not share a conference with us
        puser = self.users.findByIdentity(senderIdentity)
        if not puser:
            return # hmm we did get a notification, but we didn't see the user before
        self.conferences.userRemove(target, puser)
        if self.conferences.userLocate(puser):
            self.users.remove(senderIdentity)
        event(self, 'partedConference', user=puser, target=target, reason=reason)
        del puser

    #
    # Callbacks, you can use this to extend your transport with callback features
    #

    def addCallback(self, command, event, callback):
        if not self.callbacks.has_key(command):
            self.callbacks[command] = []
        self.callbacks[command].append([callback, event])

class ITransportManager:
    """Keeps track of all transports."""

    def __init__(self):
        self.config     = None
        self.started    = time.time()
        self.transports = []
        self.loadedMods = False

    def loadModules(self):
        if self.config and not self.loadedMods:
            if not self.config.sectionExists('general'):
                return
            moduleDirs = self.config.get('general', 'modules')
            skipModule = self.config.get('general', 'skipmodule')
            if not moduleDirs:
                log.msg(_('Could not find config option "%s" in "%s", starting without modules') % ('module', 'general'))
                return
            if not skipModule:
                skipModule = []
            for moduleDir in moduleDirs:
                moduleManager.loadDir(moduleDir, [x for x in skipModule if x.startswith(moduleDir)])
            self.loadedMods = True

    def addTransport(self, protocol, name, config):
        """Add a new transport instance to the pool of transports."""
        transportModule = 'ddb.transport.%s' % protocol
        try:
            module = localImport(transportModule)
        except ImportError, e:
            log.err(_('Could not load transport %s: %s') % (protocol, str(e)))
            logException(e)
            return
        log.msg(_('Loaded transport %s') % protocol)
        try:
            transport = getattr(module, 'transport')
        except AttributeError, e:
            log.err(_('Module %s has no class "transport", fix it') % transportModule)
            return
        newTransport = transport(self, name, config)
        self.transports.append(newTransport)

    def loadConfig(self, config):
        self.config = configFile(config)
        self.config.load(False)
        if not self.config.sectionExists('general'):
            log.msg(_('Could not find config section "general" in %s') % config)
            return False
        log.msg(_('Finding servers to connect to in %s') % config, debug=True)
        if not self.config.get('general', 'connect'):
            log.msg(_('Could not find config option "%s" in "%s" in %s, doing nothing') % ('general', 'connect', config))
            return False
        print self.config.get('general', 'connect')
        for server in self.config.get('general', 'connect'):
            self.loadTransportFromConfig(server)
        return True

    def loadTransportFromConfig(self, name):
        section = 'server:%s' % name
        if not self.config.sectionExists(section):
            log.msg(_('Could not find section "%s"') % section)
            return
        # find transport type
        transportName = self.config.get(section, 'transport')
        if not transportName:
            log.msg(_('Could not find transport in section "%s"') % section)
            return
        self.addTransport(transportName[0], name, self.config.sectionGet(section))

    def startTransports(self):
        """Give all transports the start signal."""
        for transport in self.transports:
            transport.start()

    def run(self):
        """Run all reactors."""
        self.loadModules()
        reactor.run()

transportManager = ITransportManager()

# vim:ts=4:sw=4:et:
